import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;

public class Alphabet {


    public static final Alphabet BINARY = new Alphabet("01");
    public static final Alphabet OCTAL = new Alphabet("01234567");
    public static final Alphabet DECIMAL = new Alphabet("0123456789");
    public static final Alphabet HEXADECIMAL = new Alphabet("0123456789ABCDEF");
    public static final Alphabet DNA = new Alphabet("ACGT");
    public static final Alphabet LOWERCASE = new Alphabet("abcdefghijklmnopqrstuvwxyz");
    public static final Alphabet UPPERCASE = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    public static final Alphabet PROTEIN = new Alphabet("ACDEFGHIKLMNPQRSTVWY");
    public static final Alphabet BASE64 = 
        new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
    public static final Alphabet ASCII = new Alphabet(128);
    public static final Alphabet EXTENDED_ASCII = new Alphabet(256);
    public static final Alphabet UNICODE16 = new Alphabet(65536);


    private char[] alphabet;
    private int[] indices;
    private final int R; //

    public Alphabet(String alpha) {
        
        boolean[] unicode = new boolean[Character.MAX_VALUE];
        for(int i = 0; i < alpha.length(); i++) {
            char c = alpha.charAt(i);
            if(unicode[c]) 
                throw new IllegalArgumentException("Illegal alphabet: repeated character = '" + c + "'");
            unicode[c] = true;
        }

        alphabet = alpha.toCharArray();

        R = alpha.length();
        indices = new int[Character.MAX_VALUE];
        for(int i = 0; i < indices.length; i++) {
            indices[i] = -1;
        }

        for(int c = 0; c < R; c++) {
            indices[alphabet[c]] = c;
        }

        } 

        private Alphabet(int radix) {
            this.R = radix;
            alphabet = new char[R];
            indices = new int[R];

            for(int i = 0; i < R; i++) {
                alphabet[i] = (char) i;
                indices[i] = i;
            }
        }
        public Alphabet() {
            this(256);
        }

        public boolean contains(char c) {
            return indices[c] != -1;
        }

        @Deprecated
        public int R() {
            return R;
        }

        public int radix() {
            return R;
        }

        public int lgR() {
            int lgR = 0;
            for(int t = R - 1; t >= 1; t /= 2) {
                lgR++;
            }
            return lgR;
        }

        public int toIndex(char c) {
            if(c >= indices.length || indices[c] == -1)
                throw new IllegalArgumentException("Character " + c + " not in a alphabet");

            return indices[c];
        }

        public int[] toIndices(String s) {
            char[] chs = s.toCharArray();
            int[] idx = new int[s.length()];
            for(int i = 0; i < idx.length; i++) {
                idx[i] = toIndex(chs[i]);
            }

            return idx;
        }

        public char toChar(int index) {
            if(index < 0 || index >= R) {
                throw new IllegalArgumentException("index must be between 0 and " + R + ":" + index);
            }
            return alphabet[index];
        }

        public String toChars(int[] indxs) {
            StringBuilder s = new StringBuilder(indxs.length);
            for(int i = 0; i < indxs.length; i++) {
                s.append(toChar(indxs[i]));
            }
            return s.toString();
        }
    
        public static void main(String[] args) {
            int[] encoded1 = Alphabet.BASE64.toIndices("NowIsTheTimeForAllGoodMen");
            String decoded1 = Alphabet.BASE64.toChars(encoded1);
            StdOut.println(decoded1);
            int[] encoded2 = Alphabet.DNA.toIndices("AACGAACGGTTTACCCCG");
            String decoded2 = Alphabet.DNA.toChars(encoded2);
            StdOut.println(decoded2);
            int[] encoded3 = Alphabet.DECIMAL.toIndices("01234567890123456789");
            String decoded3 = Alphabet.DECIMAL.toChars(encoded3);
            StdOut.println(decoded3);
        }
 }
    


